package com.gxa.admin.service.impl;

import com.google.common.collect.ImmutableMap;
import com.gxa.admin.exception.RoleNotFoundException;
import com.gxa.admin.exception.UsernameAlreadyExistException;
import com.gxa.admin.exception.UsernameNotFoundException;
import com.gxa.admin.pojo.Emp;
import com.gxa.admin.pojo.EmpRole;
import com.gxa.admin.pojo.Role;
import com.gxa.admin.pojo.dto.EmpAddDto;
import com.gxa.admin.pojo.dto.EmpUpdateDto;
import com.gxa.admin.pojo.enums.RoleType;
import com.gxa.admin.repo.EmpRepo;
import com.gxa.admin.repo.EmpRoleRepo;
import com.gxa.admin.repo.RoleRepo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Optional;

/**
 * @see UserDetailsService
 * @see UserDetails
 * @author IKE <hp1041735092@outlook.jp>
 * @date 22/3/2024
 */

@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class EmpService {
    private final EmpRepo empRepo;
    private final RoleRepo roleRepo;
    private final EmpRoleRepo empRoleRepo;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 添加后台用户,默认绑定部分权限
     */
    @Transactional(rollbackFor = Exception.class)
    public void save(EmpAddDto empAddDto){
        //确保用户名不存在
        ensureAccountNotExist(empAddDto.getEmployeeAccount());
        Emp emp = empAddDto.toEmp();
        log.info(emp.toString());
        //对输入的密码进行加密
        emp.setEmployeePwd(bCryptPasswordEncoder.encode(empAddDto.getEmployeePwd()));
        empRepo.save(emp);

        //绑定默认初始角色
        Role empRole = roleRepo.findByRoleName(RoleType.USER.getName()).orElseThrow( () ->
                new RoleNotFoundException(ImmutableMap.of("roleName",RoleType.USER.getName())));
        Role manageRole = roleRepo.findByRoleName(RoleType.MANAGER.getName()).orElseThrow( () ->
                new RoleNotFoundException(ImmutableMap.of("roleName",RoleType.MANAGER.getName())));
        empRoleRepo.save(new EmpRole(emp,empRole));
        empRoleRepo.save(new EmpRole(emp,manageRole));
    }

    /**
     * 根据员工账户名查询员工
     */
    public static final String USERNAME = "username:";
    public Emp find(String employeeAccount) {
        return empRepo.findByEmployeeAccount(employeeAccount)
                .orElseThrow(() -> new UsernameNotFoundException(ImmutableMap
                                .of(USERNAME,employeeAccount)));
    }

    /**
     * 修改员工信息对修改后的密码进行加密
     * 对应值留空则不修改,不可修改账号和id
     */
    @Transactional(rollbackFor = Exception.class)
    public void update(EmpUpdateDto empUpdateDto){
        Emp emp = empRepo.findById(empUpdateDto.getEmployeeId())
                .orElseThrow(() -> new UsernameNotFoundException
                        (ImmutableMap.of(USERNAME, empUpdateDto.getEmployeeName())));
        if (StringUtils.hasLength(empUpdateDto.getEmployeeName())){ //设置了新名称 -> 写入
            emp.setEmployeeName(empUpdateDto.getEmployeeName());
        }
        if (StringUtils.hasLength(empUpdateDto.getEmployeePwd())){  //设置了新密码 -> 加密并写入
            String encodedPwd = bCryptPasswordEncoder.encode(empUpdateDto.getEmployeePwd());
            emp.setEmployeePwd(encodedPwd);
        }
        if (StringUtils.hasLength(empUpdateDto.getEmployeeTel())){  //设置了新电话 -> 写入
            emp.setEmployeeTel(empUpdateDto.getEmployeeTel());
        }
    }

    /**
     * 删除员工,如果缓存中存在token,需进行删除
     * fixme: 如果是分布式缓存,不要直接删除;在缓存中选一个分片做黑名单,将其放入黑名单中,直到token过期,再进行删除(避免一致性问题);添加一个过滤器,每次用户请求,校验token是否存在于黑名单中
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(Integer employeeId){
        Emp emp = empRepo.findById(employeeId).orElseThrow(() ->
                new UsernameNotFoundException(ImmutableMap.of(USERNAME,employeeId)));
        empRepo.deleteById(employeeId);
        stringRedisTemplate.delete(emp.getEmployeeAccount());
    }

    /**
     * 校验密码
     */
    public boolean check(String rawPwd, String cryptPwd) {
        return this.bCryptPasswordEncoder.matches(rawPwd, cryptPwd);
    }

    /**
     * 添加用户时,需要确保当前添加的用户名不存在
     */
    private void ensureAccountNotExist(String employeeAccount){
        boolean exist = empRepo.findByEmployeeAccount(employeeAccount).isPresent();
        if (exist){
            throw new UsernameAlreadyExistException(ImmutableMap.of(USERNAME,employeeAccount));
        }
    }
}
